/*
 * Copyright (c) 2022, IMMORTA Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice, this list
 *   of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright notice, this
 *   list of conditions and the following disclaimer in the documentation and/or
 *   other materials provided with the distribution.
 *
 * - Neither the name of IMMORTA Inc. nor the names of its
 *   contributors may be used to endorse or promote products derived from this
 *   software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*!
 * @file uart_lin_drv.c
 * @brief This file defines ulin driver functions
 */

/*******Includes***************************************************************/
#include "uart_lin_reg_access.h"
#include "uart_reg_access.h"
#include "uart_lin_drv.h"
#include "clock_drv.h"

/*******Definitions************************************************************/
#define UART_LIN_SYNC_FIELD         (0x55U) /*!< LIN SYNC byte value */
#define UART_LIN_MAX_TIMEOUT_CNT    (0xFFFFUL) /*!< LIN time out cnt */

/*******Variables**************************************************************/
/*! @brief Table to save register base addresses for UART instances */
static UART_Type* const s_ulinRegBase[CONFIG_UART_INSTANCE_COUNT] = CONFIG_UART_REG_BASE_PTRS;

/*! @brief Table to save UART IRQ for UART instances */
static const IRQn_Type s_ulinIrqId[CONFIG_UART_INSTANCE_COUNT] = CONFIG_UART_IRQS;

/*! @brief Table to save UART clock names for UART instances */
static const clk_names_t s_ulinClkName[CONFIG_UART_INSTANCE_COUNT] = CONFIG_UART_CLOCK_NAMES;

/*! @brief Table to save UART names for UART instances */
static const module_names_t s_ulinModuleName[CONFIG_UART_INSTANCE_COUNT] = CONFIG_UART_MODULE_NAMES;

/*! @brief Table to save LIN user config structure pointers */
static lin_config_t* s_ulinConfigPtr[CONFIG_UART_INSTANCE_COUNT] = {NULL};

/*! @brief Table to save UART state structure pointers */
static lin_state_t* s_ulinStatePtr[CONFIG_UART_INSTANCE_COUNT] = {NULL};

/*! @brief Table to save LIN wakeup signal data */
static uint8_t s_ulinWakeupSignal[CONFIG_UART_INSTANCE_COUNT] = {0U};

/*! @brief Table to save the BAUD register value */
static uint32_t s_ulinRegOfBAUD[CONFIG_UART_INSTANCE_COUNT] = {0U};

/*! @brief Table to save LIN bus state */
static lin_bus_state_t s_ulinBusState[CONFIG_UART_INSTANCE_COUNT] = {LIN_BUS_STATE_NORMAL};

/*******Prototypes*************************************************************/

/*******Private Functions******************************************************/
static void UART_LIN_ProcessBreakDetect(uint32_t instance);
static void UART_LIN_ProcessFrame(uint32_t instance,
                                  uint8_t tmpByte);
static void UART_LIN_ProcessFrameHeader(uint32_t instance,
                                        uint8_t tmpByte);
static void UART_LIN_ProcessReceiveFrameData(uint32_t instance,
                                             uint8_t tmpByte);
static void UART_LIN_ProcessSendFrameData(uint32_t instance,
                                          uint8_t tmpByte);
static uint8_t UART_LIN_MakeChecksumByte(uint32_t instance,
                                         const uint8_t* buffer,
                                         uint8_t sizeBuffer,
                                         uint8_t PID);

/*******Code*******************************************************************/

/*******************************************************************************
 *** Function Name : UART_LIN_Init
 *** Description   : Initializes an instance LIN Hardware Interface for LIN Network
 ******************************************************************************/
errcode_t UART_LIN_Init(uint32_t instance,
                        lin_state_t* ulinState,
                        lin_config_t* ulinConfig)
{
    ASSERT_PARAM((ulinConfig != NULL) && (ulinState != NULL));
    ASSERT_PARAM(instance < CONFIG_UART_INSTANCE_COUNT);

    uint32_t ulinClockFreq = 0U;
    errcode_t errCode = ERR_SUCCESS;

    /* Get register base address of the UART instance */
    UART_Type* regBase = s_ulinRegBase[instance];
    /* Save runtime structure pointer */
    s_ulinStatePtr[instance] = ulinState;
    /* Save LIN user config structure pointer */
    s_ulinConfigPtr[instance] = ulinConfig;

    /* Enable clock of the UART module */
    CLOCK_SetModuleBusClockEnable(s_ulinModuleName[instance], true);
    /* Set soft reset for UART module to inactive */
    CLOCK_SetModuleSoftResetState(s_ulinModuleName[instance], RESET_INACTIVE);

    /* Get the UART clock as configured in the clock drv */
    (void)CLOCK_GetFreq(s_ulinClkName[instance], &ulinClockFreq);

    if ((ulinClockFreq / 16U) < ulinConfig->baudRate) {
        errCode = ERR_ERROR;
    } else {
        /* Create txCompleted and rxCompleted semaphores as counting, with init value is 0 */
        if (OS_Adapter_CreateSemaphore(&ulinState->txCompleted, 0) != ERR_SUCCESS) {
            errCode = ERR_ERROR;
        } else if (OS_Adapter_CreateSemaphore(&ulinState->rxCompleted, 0) != ERR_SUCCESS) {
            /* to fix the GCC compile warning, use the macro to distinguish */
#ifdef USING_OS_FREERTOS
            if (ulinState->txCompleted != NULL) {
#endif
                (void)OS_Adapter_DestroySemaphore(&ulinState->txCompleted);
#ifdef USING_OS_FREERTOS
                ulinState->txCompleted = NULL;
            }
#endif
            errCode = ERR_ERROR;
        } else {
            /* Set baud rate to User's value */
            UART_REG_SetOversamplingRate(regBase, UART_OVERSAMPLE_16);
            /* Add 0.5 * overSampleRate * TargetBaudRate for rounding the divisor */
            UART_REG_SetBaudRateDivisor(regBase, ((ulinClockFreq << UART_BAUD_SCALED_SHIFT) + ulinConfig->baudRate * 8U)
                                        / 16U / ulinConfig->baudRate);
            s_ulinRegOfBAUD[instance] = UART_REG_GetBAUD(regBase);

            /* Set 8 bit counts per char */
            UART_REG_SetWordLength(regBase, UART_WORD_LEN_8BIT);
            /* Set no parity mode */
            UART_REG_SetParityModel(regBase, UART_PARITY_DISABLED);
            /* Set two stop bit for inter-byte space 1bit*/
            UART_REG_SetStopBitCount(regBase, UART_TWO_STOP_BIT);

            /* Check if the current node is MASTER */
            if (ulinConfig->nodeMode == LIN_MODE_MASTER) {
                /* Set Break char length as 13 bits minimum */
                UART_LIN_REG_SetTransmitBreakCharLength(regBase, LIN_BREAK_CHAR_TRANSMIT_13_BIT);
            }

            /* if autobaud is enabled */
            if ((ulinConfig->autobaud) && (ulinConfig->nodeMode == LIN_MODE_SLAVE)) {
                /* Enable LIN auto baud rate function */
                UART_LIN_REG_SetAutoBaudEnable(regBase, true);
                /* Enable LIN sync error interrupt */
                UART_LIN_REG_SetSyncErrorIntEnable(regBase, true);
            }

            /* Set Break char detect length as 11 bits minimum */
            UART_LIN_REG_SetBreakCharDetectLength(regBase, LIN_BREAK_CHAR_DETECT_11_BIT);
            /* Enable RX complete interrupt */
            UART_REG_SetIntEnable(regBase, UART_INT_RX_NOT_EMPTY, true);
            /* Enable frame error interrupt */
            UART_REG_SetIntEnable(regBase, UART_INT_FRAME_ERROR, true);
            /* Enable RX overflow interrupt */
            UART_REG_SetIntEnable(regBase, UART_INT_RX_OVERFLOW, true);
            /* Enable LIN break detect interrupt */
            UART_LIN_REG_SetBreakDetectIntEnable(regBase, true);
            /* Enable LIN bus error interrupt */
            UART_LIN_REG_SetBusErrorIntEnable(regBase, true);
            /* Enable LIN function */
            UART_LIN_REG_SetLinEnable(regBase, true);

            /* Set LIN interrupt service routine */
            UART_InstallIRQHandlerProcess(instance, UART_LIN_IRQHandlerProcess);
            /* Clears UART interrupt flag and Enable UART interrupts */
            NVIC_ClearPendingIRQ(s_ulinIrqId[instance]);
            NVIC_EnableIRQ(s_ulinIrqId[instance]);

            /* Change node's current state to IDLE */
            ulinState->nodeState = LIN_NODE_STATE_IDLE;
            /* Clear flags in current LIN state structure */
            ulinState->isTxBusy = false;
            ulinState->isRxBusy = false;
            ulinState->isBusBusy = false;
            ulinState->isRxBlocking = false;
            ulinState->isTxBlocking = false;
            ulinState->timeoutCounterFlag = false;
            ulinState->timeoutCounterCalc = false;
            ulinState->timeoutCounter = 0U;
            ulinState->callback = ulinConfig->callback;
            ulinState->callbackParam = ulinConfig->callbackParam;

            /* Assign wakeup signal to satisfy LIN Specifications specifies that
             * wakeup signal shall be in range from 250us to 5ms
             */
            s_ulinWakeupSignal[instance] = 0xF0U;

            /* Enable the UART transmitter and receiver */
            UART_REG_SetTxEnable(regBase, true);
            UART_REG_SetRxEnable(regBase, true);
        }
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : LIN_UART_Deinit
 *** Description   : Shuts down the LIN Hardware Interface by disabling interrupts
 ***                 and transmitter/receiver
 ******************************************************************************/
void UART_LIN_Deinit(uint32_t instance)
{
    volatile uint32_t timeOutCnt = 0U;
    ASSERT_PARAM(instance < CONFIG_UART_INSTANCE_COUNT);

    /* Get register base address of the UART instance */
    UART_Type* regBase = s_ulinRegBase[instance];
    /* Get the current LIN state of this UART instance */
    lin_state_t* ulinCurState = s_ulinStatePtr[instance];

    /* Check if current instance is already de-initialized or is gated */
    ASSERT_PARAM(ulinCurState != NULL);

    /* Wait until the data is completely shifted out of shift register */
    while (!UART_REG_GetStatusFlag(regBase, UART_TX_COMPLETE)) {
        if (timeOutCnt++ > UART_LIN_MAX_TIMEOUT_CNT) {
            break;
        }
    }

    /* Destroy TX and RX semaphores */
    /* to fix the GCC compile warning, use the macro to distinguish */
#ifdef USING_OS_FREERTOS
    if (ulinCurState->txCompleted != NULL) {
        (void)OS_Adapter_DestroySemaphore(&ulinCurState->txCompleted);
        ulinCurState->txCompleted = NULL;
    }
    if (ulinCurState->rxCompleted != NULL) {
        (void)OS_Adapter_DestroySemaphore(&ulinCurState->rxCompleted);
        ulinCurState->rxCompleted = NULL;
    }
#else
    (void)OS_Adapter_DestroySemaphore(&ulinCurState->txCompleted);
    (void)OS_Adapter_DestroySemaphore(&ulinCurState->rxCompleted);
#endif

    /* Set soft reset for UART module to active */
    CLOCK_SetModuleSoftResetState(s_ulinModuleName[instance], RESET_ACTIVE);
    /* Disable clock of the UART module */
    CLOCK_SetModuleBusClockEnable(s_ulinModuleName[instance], false);

    /* Disable UART interrupts and clear interrupt flag */
    NVIC_DisableIRQ(s_ulinIrqId[instance]);
    NVIC_ClearPendingIRQ(s_ulinIrqId[instance]);

    /* Change node's current state to UNINIT */
    ulinCurState->nodeState = LIN_NODE_STATE_UNINIT;
    /* Clear our saved pointer to the LIN state structure */
    s_ulinStatePtr[instance] = NULL;
}

/*******************************************************************************
 *** Function Name : UART_LIN_GetDefaultConfig
 *** Description   : Initializes the LIN user configuration structure with default values
 ******************************************************************************/
void UART_LIN_GetDefaultConfig(bool isMaster,
                               lin_config_t* ulinConfig)
{
    ASSERT_PARAM(ulinConfig != NULL);

    ulinConfig->baudRate = 19200U;
    ulinConfig->classicPID = NULL;
    ulinConfig->numOfClassicPID = 0U;
    ulinConfig->callback = NULL;
    ulinConfig->callbackParam = NULL;

    if (isMaster == true) {
        ulinConfig->autobaud = false;
        ulinConfig->nodeMode = LIN_MODE_MASTER;
    } else {
        ulinConfig->autobaud = true;
        ulinConfig->nodeMode = LIN_MODE_SLAVE;
    }
}

/*******************************************************************************
 *** Function Name : UART_LIN_InstallCallback
 *** Description   : Installs callback function that is used for LIN_IRQHandlerProcess
 ******************************************************************************/
void UART_LIN_InstallCallback(uint32_t instance,
                              lin_callback_t callback,
                              void* param)
{
    ASSERT_PARAM(instance < CONFIG_UART_INSTANCE_COUNT);

    /* Get the current LIN state of this UART instance */
    lin_state_t* ulinState = s_ulinStatePtr[instance];

    /* Install new callback function */
    ulinState->callback = callback;
    ulinState->callbackParam = param;
}

/*******************************************************************************
 *** Function Name : UART_LIN_SendFrameDataBlocking
 *** Description   : Sends Frame data out through the LIN Hardware using blocking method
 ******************************************************************************/
errcode_t UART_LIN_SendFrameDataBlocking(uint32_t instance,
                                         const uint8_t* txBuff,
                                         uint8_t txSize,
                                         uint32_t timeoutmSec)
{
    ASSERT_PARAM(txBuff != NULL);
    ASSERT_PARAM(instance < CONFIG_UART_INSTANCE_COUNT);

    errcode_t errCode = ERR_SUCCESS;
    /* Get register base address of the UART instance */
    UART_Type* regBase = s_ulinRegBase[instance];
    /* Get the current LIN state of this UART instance */
    lin_state_t* ulinCurState = s_ulinStatePtr[instance];

    /* Check whether current state is sleep */
    bool checkSleepMode = (LIN_NODE_STATE_SLEEP == ulinCurState->nodeState);

    /* Check if txSize > 8 or equal to 0 or node's current state
     * is in SLEEP then return ERR_ERROR */
    if ((8U < txSize) || (0U == txSize) || checkSleepMode) {
        errCode = ERR_ERROR;
    } else {
        /* Check if the LIN Bus is busy */
        if (ulinCurState->isBusBusy) {
            errCode = ERR_BUSY;
        } else {
            /* Make the checksum byte. */
            ulinCurState->checkSum = UART_LIN_MakeChecksumByte(instance, txBuff, txSize, ulinCurState->pid);

            /* Update the LIN state structure. */
            ulinCurState->txBuff = txBuff;
            /* Add a place for checksum byte */
            ulinCurState->txSize = (uint8_t)(txSize + 1U);
            ulinCurState->cntByte = 0U;
            ulinCurState->isBusBusy = true;
            ulinCurState->isTxBusy = true;
            ulinCurState->isTxBlocking = true;

            /* Set node's current state to SEND_DATA */
            ulinCurState->nodeState = LIN_NODE_STATE_SEND_DATA;
            /* Start sending data */
            UART_REG_PutData(regBase, (*ulinCurState->txBuff));

            /* Wait until the transmission is complete */
            if (OS_Adapter_WaitSemaphore(&ulinCurState->txCompleted, timeoutmSec) == ERR_TIMEOUT) {
                errCode = ERR_TIMEOUT;
            }

            /* Clear Tx Blocking flag */
            ulinCurState->isTxBlocking = false;
            /* Clear Tx Busy flag */
            ulinCurState->isTxBusy = false;
            /* Change node's current state to IDLE */
            (void)UART_LIN_GotoIdleState(instance);
        }
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : UART_LIN_SendFrameData
 *** Description   : Sends frame data out through the LIN Hardware using non-blocking method
 ******************************************************************************/
errcode_t UART_LIN_SendFrameData(uint32_t instance,
                                 const uint8_t* txBuff,
                                 uint8_t txSize)
{
    ASSERT_PARAM(txBuff != NULL);
    ASSERT_PARAM(instance < CONFIG_UART_INSTANCE_COUNT);

    errcode_t errCode = ERR_SUCCESS;
    /* Get register base address of the UART instance */
    UART_Type* regBase = s_ulinRegBase[instance];
    /* Get the current LIN state of this UART instance */
    lin_state_t* ulinCurState = s_ulinStatePtr[instance];

    /* Check whether current state is sleep */
    bool checkSleepMode = (LIN_NODE_STATE_SLEEP == ulinCurState->nodeState);

    /* Check if txSize > 8 or equal to 0 or node's current state is in SLEEP then return ERR_ERROR */
    if ((8U < txSize) || (0U == txSize) || checkSleepMode) {
        errCode = ERR_ERROR;
    } else {
        /* Check if the LIN Bus is busy */
        if (ulinCurState->isBusBusy) {
            errCode = ERR_BUSY;
        } else {
            /* Make the checksum byte */
            ulinCurState->checkSum = UART_LIN_MakeChecksumByte(instance, txBuff, txSize, ulinCurState->pid);

            /* Update the LIN state structure */
            ulinCurState->txBuff = txBuff;
            /* Add a place for checksum byte */
            ulinCurState->txSize = (uint8_t)(txSize + 1U);
            ulinCurState->cntByte = 0U;
            ulinCurState->nodeState = LIN_NODE_STATE_SEND_DATA;
            ulinCurState->isBusBusy = true;
            ulinCurState->isTxBusy = true;

            /* Start sending data */
            UART_REG_PutData(regBase, (*ulinCurState->txBuff));
        }
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : UART_LIN_GetTransmitStatus
 *** Description   : Get status of an on-going non-blocking transmission
 ******************************************************************************/
errcode_t UART_LIN_GetTransmitStatus(uint32_t instance,
                                     uint8_t* bytesRemaining)
{
    ASSERT_PARAM(instance < CONFIG_UART_INSTANCE_COUNT);

    errcode_t errCode = ERR_SUCCESS;
    /* Get the current LIN state of this UART instance */
    const lin_state_t* ulinCurState = s_ulinStatePtr[instance];

    /* Get the number of bytes that is still needed to transmit */
    *bytesRemaining = (uint8_t)(ulinCurState->txSize - ulinCurState->cntByte);

    /* Return status of the on-going transmission */
    if ((ulinCurState->nodeState == LIN_NODE_STATE_SEND_DATA) && ((*bytesRemaining) != 0U)) {
        if (ulinCurState->timeoutCounterFlag == false) {
            errCode = ERR_BUSY;
        } else {
            errCode = ERR_TIMEOUT;
        }
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : UART_LIN_RecvFrmDataBlocking
 *** Description   : Receives frame data through the LIN Hardware using blocking method
 ******************************************************************************/
errcode_t UART_LIN_RecvFrmDataBlocking(uint32_t instance,
                                       uint8_t* rxBuff,
                                       uint8_t rxSize,
                                       uint32_t timeoutmSec)
{
    ASSERT_PARAM(rxBuff != NULL);
    ASSERT_PARAM(instance < CONFIG_UART_INSTANCE_COUNT);

    errcode_t errCode = ERR_SUCCESS;
    /* Get the current LIN state of this UART instance */
    lin_state_t* ulinCurState = s_ulinStatePtr[instance];

    /* Check whether current state is sleep */
    bool checkSleepMode = (LIN_NODE_STATE_SLEEP == ulinCurState->nodeState);

    /* Check if rxSize > 8 or equal to 0 or node's current state is in SLEEP state then return ERR_ERROR */
    if ((8U < rxSize) || (0U == rxSize) || checkSleepMode) {
        errCode = ERR_ERROR;
    } else {
        /* Check if the LIN Rx is busy */
        if (ulinCurState->isRxBusy) {
            errCode = ERR_BUSY;
        } else {
            /* Update the LIN state structure */
            ulinCurState->rxBuff = rxBuff;
            /* Add a place for checksum byte */
            ulinCurState->rxSize = (uint8_t)(rxSize + 1U);
            ulinCurState->cntByte = 0U;
            /* Setup receiving data blocking */
            ulinCurState->nodeState = LIN_NODE_STATE_RECV_DATA;
            ulinCurState->isBusBusy = true;
            ulinCurState->isRxBusy = true;
            ulinCurState->isRxBlocking = true;

            /* Wait until the reception is complete */
            if (OS_Adapter_WaitSemaphore(&ulinCurState->rxCompleted, timeoutmSec) == ERR_TIMEOUT) {
                errCode = ERR_TIMEOUT;
            }

            /* In case of receiving a go to sleep request, after callback, node is in SLEEP state */
            if (ulinCurState->nodeState != LIN_NODE_STATE_SLEEP) {
                /* Update node's current state to IDLE */
                ulinCurState->nodeState = LIN_NODE_STATE_IDLE;
            }

            /* Clear Rx Blocking flag. */
            ulinCurState->isRxBlocking = false;
            /* Update Bus Busy flag. */
            ulinCurState->isBusBusy = false;
            ulinCurState->isRxBusy = false;
        }
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : UART_LIN_RecvFrmData
 *** Description   : Receives frame data through the LIN Hardware using non-blocking method
 ******************************************************************************/
errcode_t UART_LIN_RecvFrmData(uint32_t instance,
                               uint8_t* rxBuff,
                               uint8_t rxSize)
{
    ASSERT_PARAM(rxBuff != NULL);
    ASSERT_PARAM(instance < CONFIG_UART_INSTANCE_COUNT);

    errcode_t errCode = ERR_SUCCESS;
    /* Get the current LIN state of this UART instance. */
    lin_state_t* ulinCurState = s_ulinStatePtr[instance];

    /* Check whether current state is sleep */
    bool checkSleepMode = (LIN_NODE_STATE_SLEEP == ulinCurState->nodeState);

    /* Check if rxSize > 8 or equal to 0 or node's current state is in SLEEP state then return ERR_ERROR */
    if ((8U < rxSize) || (0U == rxSize) || checkSleepMode) {
        errCode = ERR_ERROR;
    } else {
        /* Check if the LIN Bus is busy */
        if (ulinCurState->isBusBusy) {
            errCode = ERR_BUSY;
        } else {
            /* Update the LIN state structure */
            ulinCurState->rxBuff = rxBuff;
            /* Add a place for checksum byte */
            ulinCurState->rxSize = (uint8_t)(rxSize + 1U);
            ulinCurState->cntByte = 0U;

            /* Start receiving data */
            ulinCurState->nodeState = LIN_NODE_STATE_RECV_DATA;
            ulinCurState->isBusBusy = true;
            ulinCurState->isRxBusy = true;
            ulinCurState->isRxBlocking = false;
        }
    }

    return errCode;
}


/*******************************************************************************
 *** Function Name : UART_LIN_GetReceiveStatus
 *** Description   : Get status of an on-going non-blocking reception
 ******************************************************************************/
errcode_t UART_LIN_GetReceiveStatus(uint32_t instance,
                                    uint8_t* bytesRemaining)
{
    ASSERT_PARAM(instance < CONFIG_UART_INSTANCE_COUNT);

    errcode_t errCode = ERR_SUCCESS;
    /* Get the current LIN state of this UART instance */
    const lin_state_t* ulinCurState = s_ulinStatePtr[instance];
    /* Get the number of bytes that is still needed to receive */
    *bytesRemaining = (uint8_t)(ulinCurState->rxSize - ulinCurState->cntByte);

    /* Return status of the on-going reception */
    if ((ulinCurState->nodeState == LIN_NODE_STATE_RECV_DATA) && ((*bytesRemaining) != 0U)) {
        if (ulinCurState->timeoutCounterFlag == false) {
            errCode = ERR_BUSY;
        } else {
            errCode = ERR_TIMEOUT;
        }
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : UART_LIN_AbortTransferData
 *** Description   : Aborts an on-going non-blocking transmission/reception
 ******************************************************************************/
void UART_LIN_AbortTransferData(uint32_t instance)
{
    ASSERT_PARAM(instance < CONFIG_UART_INSTANCE_COUNT);

    /* Get the current LIN state of this UART instance */
    lin_state_t* ulinCurState = s_ulinStatePtr[instance];

    /* Change node's current state to IDLE */
    (void)UART_LIN_GotoIdleState(instance);

    /* Clear LIN Tx and Rx Busy flag */
    ulinCurState->isTxBusy = false;
    ulinCurState->isRxBusy = false;
    ulinCurState->isBusBusy = false;
}

/*******************************************************************************
 *** Function Name : UART_LIN_GoToSleepState
 *** Description   : Puts current LIN node to sleep mode
 ******************************************************************************/
void UART_LIN_GoToSleepState(uint32_t instance)
{
    ASSERT_PARAM(instance < CONFIG_UART_INSTANCE_COUNT);

    /* Get register base address of the UART instance */
    UART_Type* regBase = s_ulinRegBase[instance];
    /* Get the current LIN state of this UART instance */
    lin_state_t* ulinCurState = s_ulinStatePtr[instance];

    /* Disable RX complete interrupt */
    UART_REG_SetIntEnable(regBase, UART_INT_RX_NOT_EMPTY, false);
    /* Disable frame error interrupt */
    UART_REG_SetIntEnable(regBase, UART_INT_FRAME_ERROR, false);

    /* Update node's current state to SLEEP */
    ulinCurState->nodeState = LIN_NODE_STATE_SLEEP;
    /* Clear txBusy, rxBusy, busBusy flags */
    ulinCurState->isTxBusy  = false;
    ulinCurState->isRxBusy  = false;
    ulinCurState->isBusBusy = false;
}

/*******************************************************************************
 *** Function Name : UART_LIN_GotoIdleState
 *** Description   : Puts current LIN node to Idle state
 ******************************************************************************/
void UART_LIN_GotoIdleState(uint32_t instance)
{
    /* Get register base address of the UART instance */
    UART_Type* regBase = s_ulinRegBase[instance];

    /* Get the current LIN state of this UART instance */
    lin_state_t* ulinCurState = s_ulinStatePtr[instance];

    if (ulinCurState->nodeState == LIN_NODE_STATE_SLEEP) {
        /* Enable RX complete interrupt */
        UART_REG_SetIntEnable(regBase, UART_INT_RX_NOT_EMPTY, true);
        /* Enable frame error interrupt */
        UART_REG_SetIntEnable(regBase, UART_INT_FRAME_ERROR, true);
    }

    /* Change node's current state to IDLE */
    ulinCurState->nodeState = LIN_NODE_STATE_IDLE;
    /* Clear Bus busy Flag */
    ulinCurState->isBusBusy = false;
    ulinCurState->isRxBusy = false;
    ulinCurState->isTxBusy = false;
}

/*******************************************************************************
 *** Function Name : UART_LIN_SendWakeupSignal
 *** Description   : Sends a wakeup signal through the LIN Hardware
 ******************************************************************************/
errcode_t UART_LIN_SendWakeupSignal(uint32_t instance)
{
    ASSERT_PARAM(instance < CONFIG_UART_INSTANCE_COUNT);

    errcode_t errCode = ERR_SUCCESS;
    /* Get register base address of the UART instance */
    UART_Type* regBase = s_ulinRegBase[instance];
    /* Get the current LIN state of this UART instance */
    const lin_state_t* ulinCurState = s_ulinStatePtr[instance];

    /* Check if bus is not busy */
    if (ulinCurState->isBusBusy == false) {
        /* Send a wakeup signal */
        UART_REG_PutData(regBase, s_ulinWakeupSignal[instance]);
    } else {
        errCode = ERR_BUSY;
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : UART_LIN_GetNodeState
 *** Description   : Get the current LIN node state
 ******************************************************************************/
lin_node_state_t UART_LIN_GetNodeState(uint32_t instance)
{
    ASSERT_PARAM(instance < CONFIG_UART_INSTANCE_COUNT);

    lin_node_state_t retVal = LIN_NODE_STATE_UNINIT;
    /* Get the current LIN state of this UART instance */
    const lin_state_t* ulinCurState = s_ulinStatePtr[instance];

    if (ulinCurState != NULL) {
        retVal = ulinCurState->nodeState;
    }

    /* Return LIN node's current state */
    return retVal;
}

/*******************************************************************************
 *** Function Name : UART_LIN_TimeoutService
 *** Description   : Callback function for Timer Interrupt Handler
 ******************************************************************************/
void UART_LIN_TimeoutService(uint32_t instance)
{
    ASSERT_PARAM(instance < CONFIG_UART_INSTANCE_COUNT);

    /* Get the current LIN state of this UART instance */
    lin_state_t* ulinCurState = s_ulinStatePtr[instance];
    /* Get LIN node's current state */
    lin_node_state_t state = ulinCurState->nodeState;

    switch (state) {
        /* If the node is SENDING DATA */
        case LIN_NODE_STATE_SEND_DATA:

            /* Check if timeout Counter is 0 */
            if (ulinCurState->timeoutCounter == 0U) {
                /* Set timeout Counter flag */
                ulinCurState->timeoutCounterFlag = true;

                if (ulinCurState->isTxBlocking == false) {
                    /* Callback to handle timeout Counter flag */
                    if (ulinCurState->callback != NULL) {
                        ulinCurState->callback(instance, LIN_NO_EVENT, ulinCurState);
                    }

                    /* Clear Tx busy flag */
                    ulinCurState->isTxBusy = false;
                    /* Change the node's current state to IDLE */
                    (void)UART_LIN_GotoIdleState(instance);
                }
            } else {
                /* If timeout Counter is not 0, then decrease timeout Counter by one */
                ulinCurState->timeoutCounter--;
            }

            break;

        /* If the node is RECEIVING DATA */
        case LIN_NODE_STATE_RECV_DATA:

            /* Check if timeout Counter is 0 */
            if (ulinCurState->timeoutCounter == 0U) {
                /* Set timeout Counter flag */
                ulinCurState->timeoutCounterFlag = true;

                /* Check if the reception is non-blocking */
                if (ulinCurState->isRxBlocking == false) {
                    /* Callback to handle timeout Counter flag */
                    if (ulinCurState->callback != NULL) {
                        ulinCurState->callback(instance, LIN_NO_EVENT, ulinCurState);
                    }

                    /* Clear Rx busy flag */
                    ulinCurState->isRxBusy = false;
                    /* Change the node's current state to IDLE */
                    (void)UART_LIN_GotoIdleState(instance);
                }
            } else {
                /* If timeout Counter is not 0, then decrease timeout Counter by one */
                ulinCurState->timeoutCounter--;
            }

            break;

        default:
            break;
    }
}

/*******************************************************************************
 *** Function Name : UART_LIN_SetTimeoutCounter
 *** Description   : Set Value for Timeout Counter
 ******************************************************************************/
void UART_LIN_SetTimeoutCounter(uint32_t instance,
                                uint32_t timeoutVal)
{
    ASSERT_PARAM(instance < CONFIG_UART_INSTANCE_COUNT);

    /* Get the current LIN state of this UART instance */
    lin_state_t* ulinCurState = s_ulinStatePtr[instance];

    /* Clear Timeout Counter Flag */
    ulinCurState->timeoutCounterFlag = false;
    /* Set new value for Timeout Counter */
    ulinCurState->timeoutCounter = timeoutVal;
}

/*******************************************************************************
 *** Function Name : UART_LIN_MasterSendHeader
 *** Description   : Sends frame header out through the LIN Hardware
 ******************************************************************************/
errcode_t UART_LIN_MasterSendHeader(uint32_t instance,
                                    uint8_t id)
{
    ASSERT_PARAM(instance < CONFIG_UART_INSTANCE_COUNT);

    errcode_t errCode = ERR_SUCCESS;
    /* Get register base address of the UART instance */
    UART_Type* regBase = s_ulinRegBase[instance];
    /* Get the current LIN state of this UART instance */
    lin_state_t* ulinCurState = s_ulinStatePtr[instance];
    /* Get the current LIN user config structure of this UART instance */
    const lin_config_t* linConfig = s_ulinConfigPtr[instance];

    /* Check whether current state is sleep */
    bool checkSleepMode = (LIN_NODE_STATE_SLEEP == ulinCurState->nodeState);

    /* Check if the current node is slave or id is invalid or node's current state is in SLEEP state */
    if ((linConfig->nodeMode == LIN_MODE_SLAVE) || (0x3FU < id) || checkSleepMode) {
        errCode = ERR_ERROR;
    } else {
        /* Check if the LIN bus is busy */
        if (ulinCurState->isBusBusy) {
            errCode = ERR_BUSY;
        } else {
            ulinCurState->id = id;
            /* Make parity for the current ID */
            ulinCurState->pid = LIN_ProcessParity(id, LIN_MAKE_PARITY);
            /* Set LIN current state to sending Break field */
            ulinCurState->nodeState = LIN_NODE_STATE_SEND_BREAK_FIELD;
            ulinCurState->isBusBusy = true;

            /* Send break char */
            UART_LIN_REG_TransmitBreakField(regBase);
        }
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : UART_LIN_EnableInt
 *** Description   : Enables LIN hardware interrupts
 ******************************************************************************/
void LIN_LIN_SetIntEnable(uint32_t instance, bool enable)
{
    ASSERT_PARAM(instance < CONFIG_UART_INSTANCE_COUNT);

    /* Get register base address of the UART instance. */
    UART_Type* regBase = s_ulinRegBase[instance];

    /* Set RX complete interrupt */
    UART_REG_SetIntEnable(regBase, UART_INT_RX_NOT_EMPTY, enable);
    /* Set frame error interrupt */
    UART_REG_SetIntEnable(regBase, UART_INT_FRAME_ERROR, enable);
    /* Set RX overflow interrupt */
    UART_REG_SetIntEnable(regBase, UART_INT_RX_OVERFLOW, enable);
    /* Set LIN break detect interrupt */
    UART_LIN_REG_SetBreakDetectIntEnable(regBase, enable);
    /* Set LIN bus error interrupt */
    UART_LIN_REG_SetBusErrorIntEnable(regBase, enable);

    if (enable) {
        /* Clears UART interrupt flag and Enable UART interrupts */
        NVIC_ClearPendingIRQ(s_ulinIrqId[instance]);
        NVIC_EnableIRQ(s_ulinIrqId[instance]);
    } else {
        /* Disable UART interrupts and clear interrupt flag */
        NVIC_DisableIRQ(s_ulinIrqId[instance]);
        NVIC_ClearPendingIRQ(s_ulinIrqId[instance]);
    }
}

/*******************************************************************************
 *** Function Name : LIN_IRQHandlerProcess
 *** Description   : Interrupt handler for UART
 ******************************************************************************/
void UART_LIN_IRQHandlerProcess(uint32_t instance)
{
    ASSERT_PARAM(instance < CONFIG_UART_INSTANCE_COUNT);

    /* Get register base address of the UART instance */
    UART_Type* regBase = s_ulinRegBase[instance];
    /* Get the current LIN state of this UART instance */
    lin_state_t* ulinCurState = s_ulinStatePtr[instance];

    /* Exit sleep mode at any interrupt condition */
    if (ulinCurState->nodeState == LIN_NODE_STATE_SLEEP) {

        /* Callback to handle event: Received a wakeup signal */
        if (ulinCurState->callback != NULL) {
            ulinCurState->callback(instance, LIN_WAKEUP_SIGNAL, ulinCurState);
        }

        /* Change node's state to IDLE */
        (void)UART_LIN_GotoIdleState(instance);
    }

    /* If LIN break character has been detected */
    if (UART_REG_GetStatusFlag(regBase, UART_LIN_BREAK_DETECT)) {
        /* Clear LIN Break Detect Interrupt Flag */
        UART_REG_ClearStatusFlag(regBase, UART_LIN_BREAK_DETECT);
        UART_LIN_ProcessBreakDetect(instance);
    } else {
        /* If Framing Error has been detected */
        if (UART_REG_GetStatusFlag(regBase, UART_FRAME_ERROR)) {
            /* Clear Framing Error Interrupt Flag */
            UART_REG_ClearStatusFlag(regBase, UART_FRAME_ERROR);
            /* Read dummy to clear RX not empty flag, void is for misra rules */
            (void)UART_REG_GetData(regBase);

            /* Check if LIN current node state is LIN_NODE_STATE_SEND_DATA */
            if (ulinCurState->nodeState == LIN_NODE_STATE_SEND_DATA) {
                /* Callback function to handle Framing Error Event */
                if (ulinCurState->callback != NULL) {
                    ulinCurState->callback(instance, LIN_FRAME_ERROR, ulinCurState);
                }
            } else {
                /* Check if LIN current node state is LIN_NODE_STATE_RECV_DATA */
                if (ulinCurState->nodeState == LIN_NODE_STATE_RECV_DATA) {
                    /* Callback function to handle Framing Error Event */
                    if (ulinCurState->callback != NULL) {
                        ulinCurState->callback(instance, LIN_FRAME_ERROR, ulinCurState);
                    }
                }
            }

            /* Change node's state to IDLE */
            (void)UART_LIN_GotoIdleState(instance);
        } else if (UART_REG_GetStatusFlag(regBase, UART_LIN_SYNC_ERROR)) {
            /* Clear SYNC error flag */
            UART_REG_ClearStatusFlag(regBase, UART_LIN_SYNC_ERROR);

            /* Callback function to handle RX Overrun Event */
            if (ulinCurState->callback != NULL) {
                ulinCurState->callback(instance, LIN_SYNC_ERROR, ulinCurState);
            }

            /* Change node's state to IDLE */
            (void)UART_LIN_GotoIdleState(instance);
        } else {
            if (UART_REG_GetStatusFlag(regBase, UART_RX_NOT_EMPTY)) {
                /* Process data in Data Register while receive, send data */
                UART_LIN_ProcessFrame(instance, (uint8_t)UART_REG_GetData(regBase));
            }
        }
    }

    /* Get RX overrun flag */
    if (UART_REG_GetStatusFlag(regBase, UART_RX_OVERFLOW)) {
        /* Clear overrun flag */
        UART_REG_ClearStatusFlag(regBase, UART_RX_OVERFLOW);

        /* Callback function to handle RX Overrun Event */
        if (ulinCurState->callback != NULL) {
            ulinCurState->callback(instance, LIN_RX_OVERRUN, ulinCurState);
        }
    }

    /* Get LIN bus error flag */
    if (UART_REG_GetStatusFlag(regBase, UART_LIN_BUS_ERROR)) {
        UART_REG_ClearStatusFlag(regBase, UART_LIN_BUS_ERROR);
        s_ulinBusState[instance] = LIN_BUS_STATE_SHORTED_GND;
    } else {
        s_ulinBusState[instance] = LIN_BUS_STATE_NORMAL;
    }
}

/*******************************************************************************
 *** Function Name : UART_LIN_ProcessBreakDetect
 *** Description   : This function process break detect for LIN communication
 ******************************************************************************/
static void UART_LIN_ProcessBreakDetect(uint32_t instance)
{
    /* Get register base address of the UART instance */
    UART_Type* regBase = s_ulinRegBase[instance];
    /* Get the current LIN state of this UART instance */
    lin_state_t* ulinCurState = s_ulinStatePtr[instance];
    /* Get the current LIN user configure structure of this UART instance */
    const lin_config_t* linConfig = s_ulinConfigPtr[instance];

    /* Check if the current node is MASTER */
    if (linConfig->nodeMode == LIN_MODE_MASTER) {
        /* Check if LIN current node state is LIN_NODE_STATE_SEND_BREAK_FIELD */
        if (ulinCurState->nodeState == LIN_NODE_STATE_SEND_BREAK_FIELD) {
            /* Set flag LIN bus busy */
            ulinCurState->isBusBusy = true;
            /* Change the node's current state to RECEIVING SYNC FIELD */
            ulinCurState->nodeState = LIN_NODE_STATE_RECV_SYNC;
            /* Send Sync Field 0x55 */
            UART_REG_PutData(regBase, UART_LIN_SYNC_FIELD);
        }
    } else {
        /* If the current node is SLAVE */
        /* Set flag LIN bus busy */
        ulinCurState->isBusBusy = true;

        /* Callback function */
        if (ulinCurState->callback != NULL) {
            ulinCurState->callback(instance, LIN_RECV_BREAK_FIELD_OK, ulinCurState);
        }

        if (linConfig->autobaud == true) {
            /* Change the node's current state to RECEIVING PID FIELD */
            ulinCurState->nodeState = LIN_NODE_STATE_RECV_PID;
        } else {
            /* Change the node's current state to RECEIVING SYNC FIELD */
            ulinCurState->nodeState = LIN_NODE_STATE_RECV_SYNC;
        }
    }
}

/*******************************************************************************
 *** Function Name : UART_LIN_ProcessFrame
 *** Description   : Part of Interrupt handler for receiving and sending data
 ******************************************************************************/
static void UART_LIN_ProcessFrame(uint32_t instance,
                                  uint8_t tmpByte)
{
    /* Get the current LIN state of this UART instance */
    lin_state_t* ulinCurState = s_ulinStatePtr[instance];

    /* Check node's current state */
    switch (ulinCurState->nodeState) {
        /* if current state is RECEIVE SYNC FIELD */
        case LIN_NODE_STATE_RECV_SYNC:

        /* if current state is RECEIVE PID */
        case LIN_NODE_STATE_RECV_PID:
            UART_LIN_ProcessFrameHeader(instance, tmpByte);
            break;

        /* if current state is RECEIVE DATA */
        case LIN_NODE_STATE_RECV_DATA:
            UART_LIN_ProcessReceiveFrameData(instance, tmpByte);
            break;

        /* if current state is SENDING DATA */
        case LIN_NODE_STATE_SEND_DATA:
            UART_LIN_ProcessSendFrameData(instance, tmpByte);
            break;

        case LIN_NODE_STATE_RECV_HDR_COMPLETED:
            if (ulinCurState->callback != NULL) {
                ulinCurState->callback(instance, LIN_RX_STATE_ERROR, ulinCurState);
            }

            break;

        default:
            break;
    }
}

/*******************************************************************************
 *** Function Name : UART_LIN_ProcessFrameHeader
 *** Description   : Part of Interrupt handler for receiving and sending data
 ******************************************************************************/
static void UART_LIN_ProcessFrameHeader(uint32_t instance,
                                        uint8_t tmpByte)
{
    /* Get register base address of the UART instance */
    UART_Type* regBase = s_ulinRegBase[instance];
    /* Get the current LIN state of this UART instance */
    lin_state_t* ulinCurState = s_ulinStatePtr[instance];
    /* Get the current LIN user config structure of this UART instance */
    const lin_config_t* linConfig = s_ulinConfigPtr[instance];

    /* Check node's current state */
    switch (ulinCurState->nodeState) {
        /* If current state is RECEIVE SYNC FIELD */
        case LIN_NODE_STATE_RECV_SYNC:

            /* If the node is MASTER */
            if (linConfig->nodeMode == LIN_MODE_MASTER) {
                /* Check if master node sent SYNC byte correctly before send PID */
                if (tmpByte == UART_LIN_SYNC_FIELD) {
                    /* Change node's current state to RECEIVING PID */
                    ulinCurState->nodeState = LIN_NODE_STATE_RECV_PID;
                    /* Send the current PID byte */
                    UART_REG_PutData(regBase, ulinCurState->pid);
                } else {
                    /* In case of errors during header transmission, it is up to the implementer
                     * how to handle these errors (stop/continue transmission) and to decide if the
                     * corresponding response is valid or not.
                     * By default, LIN Driver set isBusBusy to false, and change node's state to IDLE.
                     */
                    /* Clear Bus bus flag */
                    ulinCurState->isBusBusy = false;
                    /* Change node's current state to IDLE */
                    ulinCurState->nodeState = LIN_NODE_STATE_IDLE;

                    /* Callback function to handle event SENT SYNC BYTE ERROR */
                    if (ulinCurState->callback != NULL) {
                        ulinCurState->callback(instance, LIN_SYNC_ERROR, ulinCurState);
                    }
                }
            } else {
                if (tmpByte == UART_LIN_SYNC_FIELD) {
                    /* Change node's current state to RECEIVE PID */
                    ulinCurState->nodeState = LIN_NODE_STATE_RECV_PID;
                } else {
                    /* Callback function to handle event RECEIVED SYNC FIELD ERROR */
                    if (ulinCurState->callback != NULL) {
                        ulinCurState->callback(instance, LIN_SYNC_ERROR, ulinCurState);
                    }

                    /* Change node's current state to IDLE */
                    (void)UART_LIN_GotoIdleState(instance);
                }
            }

            break;

        /* If current state is RECEIVE PID */
        case LIN_NODE_STATE_RECV_PID:

            /* If the node is MASTER */
            if (linConfig->nodeMode == LIN_MODE_MASTER) {
                /* Check if master node sent PID correctly */
                if (tmpByte == ulinCurState->pid) {
                    ulinCurState->nodeState = LIN_NODE_STATE_RECV_HDR_COMPLETED;

                    /* Check receiving data is blocking */
                    if (ulinCurState->isRxBlocking == false) {
                        /* Clear Bus bus flag */
                        ulinCurState->isBusBusy = false;

                        /* Callback function to handle correct PID */
                        if (ulinCurState->callback != NULL) {
                            ulinCurState->callback(instance, LIN_PID_OK, ulinCurState);
                        }
                    }
                } else {
                    /* In case of errors during header transmission, it is up to the implementer
                     * how to handle these errors (stop/continue transmission) and to decide if the
                     * corresponding response is valid or not.
                     * By default, LIN Driver set isBusBusy to false, and change node's state to IDLE.
                     */
                    /* Clear bus busy flag */
                    ulinCurState->isBusBusy = false;
                    /* Change node's current state to IDLE */
                    ulinCurState->nodeState = LIN_NODE_STATE_IDLE;

                    /* Callback function to handle event MASTER SENT PID ERROR */
                    if (ulinCurState->callback != NULL) {
                        ulinCurState->callback(instance, LIN_PID_ERROR, ulinCurState);
                    }
                }
            } else {
                /* If the node is SLAVE */
                /* Check the received PID */
                ulinCurState->id = LIN_ProcessParity(tmpByte, LIN_CHECK_PARITY);
                ulinCurState->pid = tmpByte;

                if (ulinCurState->id != 0xFFU) {
                    ulinCurState->nodeState = LIN_NODE_STATE_RECV_HDR_COMPLETED;

                    /* Check receiving data is blocking */
                    if (ulinCurState->isRxBlocking == false) {
                        /* Clear Bus bus flag */
                        ulinCurState->isBusBusy = false;

                        /* Set timeout counter if baudrate different from last time after autobaud successfully */
                        if ((linConfig->autobaud) && (UART_REG_GetBAUD(regBase) != s_ulinRegOfBAUD[instance])) {
                            s_ulinRegOfBAUD[instance] = UART_REG_GetBAUD(regBase);
                            ulinCurState->timeoutCounterCalc = true;
                            UART_GetBaudRate(instance, &s_ulinConfigPtr[instance]->baudRate);
                        }

                        /* Callback function to handle event PID correct */
                        if (ulinCurState->callback != NULL) {
                            ulinCurState->callback(instance, LIN_PID_OK, ulinCurState);
                        }
                    }
                } else {
                    /* Callback function to handle event PID incorrect */
                    if (ulinCurState->callback != NULL) {
                        ulinCurState->callback(instance, LIN_PID_ERROR, ulinCurState);
                    }

                    /* Change node's current state to IDLE */
                    (void)UART_LIN_GotoIdleState(instance);
                }
            }

            break;

        default:
            break;
    }
}

/*******************************************************************************
 *** Function Name : UART_LIN_ProcessReceiveFrameData
 *** Description   : Part of Interrupt handler for receiving and sending data
 ******************************************************************************/
static void UART_LIN_ProcessReceiveFrameData(uint32_t instance,
                                             uint8_t tmpByte)
{
    /* Get the current LIN state of this UART instance */
    lin_state_t* ulinCurState = s_ulinStatePtr[instance];

    if (ulinCurState->rxSize > (ulinCurState->cntByte + 1U)) {
        *(ulinCurState->rxBuff) = tmpByte;
        ulinCurState->rxBuff++;
    } else {
        if ((ulinCurState->rxSize - ulinCurState->cntByte) == 1U) {
            ulinCurState->checkSum = tmpByte;
        }
    }

    ulinCurState->cntByte++;

    if (ulinCurState->cntByte == ulinCurState->rxSize) {
        /* Restore rxBuffer pointer */
        ulinCurState->rxBuff -= ulinCurState->rxSize - 1U;

        if (UART_LIN_MakeChecksumByte(instance, ulinCurState->rxBuff, \
                                      (ulinCurState->rxSize - 1U), ulinCurState->pid) == ulinCurState->checkSum) {
            ulinCurState->nodeState = LIN_NODE_STATE_RECV_DATA_COMPLETED;

            /* callback function to handle RX COMPLETED */
            if (ulinCurState->callback != NULL) {
                ulinCurState->callback(instance, LIN_RX_COMPLETED, ulinCurState);
            }

            /* Check if the reception is non-blocking */
            if (ulinCurState->isRxBlocking == false) {
                /* Clear Bus busy flag */
                ulinCurState->isBusBusy = false;
                /* Clear Rx busy flag */
                ulinCurState->isRxBusy = false;

                /* In case of receiving a go to sleep request, after callback, node is in SLEEP state */
                if (ulinCurState->nodeState != LIN_NODE_STATE_SLEEP) {
                    (void)UART_LIN_GotoIdleState(instance);
                }
            } else {
                /* Post Semaphore to signal Rx Completed */
                (void)OS_Adapter_PostSemaphore(&ulinCurState->rxCompleted);
            }
        } else {
            /* callback function to handle checksum error */
            if (ulinCurState->callback != NULL) {
                ulinCurState->callback(instance, LIN_CHECKSUM_ERROR, ulinCurState);
            }

            /* Clear Rx busy flag */
            ulinCurState->isRxBusy = false;
            /* Change node's current state to IDLE */
            (void)UART_LIN_GotoIdleState(instance);
        }
    }
}

/*******************************************************************************
 *** Function Name : UART_LIN_ProcessSendFrameData
 *** Description   : Part of Interrupt handler for receiving and sending data
 ******************************************************************************/
static void UART_LIN_ProcessSendFrameData(uint32_t instance,
                                          uint8_t tmpByte)
{
    uint8_t tmpSize;
    bool sendFlag = true;
    bool tmpBuffAndSize;
    bool tmpCheckSumAndSize;

    /* Get register base address of the UART instance */
    UART_Type* regBase = s_ulinRegBase[instance];
    /* Get the current LIN state of this UART instance */
    lin_state_t* ulinCurState = s_ulinStatePtr[instance];

    /* Check if Tx data register empty flag is false */
    if (UART_REG_GetStatusFlag(regBase, UART_TX_EMPTY) == false) {

        /* callback function to handle Readback error */
        if (ulinCurState->callback != NULL) {
            ulinCurState->callback(instance, LIN_READBACK_ERROR, ulinCurState);
        }

        /* Check if the transmission is non-blocking */
        if (ulinCurState->isTxBlocking == false) {
            /* Clear Tx busy flag */
            ulinCurState->isTxBusy = false;

            /* Change node's current state to IDLE */
            (void)UART_LIN_GotoIdleState(instance);
        }

        sendFlag = false;
    } else {
        tmpSize = (uint8_t)(ulinCurState->txSize - ulinCurState->cntByte);
        tmpCheckSumAndSize = (tmpSize == 1U) && (ulinCurState->checkSum != tmpByte);
        tmpBuffAndSize = (*ulinCurState->txBuff != tmpByte) && (tmpSize != 1U);

        if (tmpBuffAndSize || tmpCheckSumAndSize) {

            /* callback function to handle Readback error */
            if (ulinCurState->callback != NULL) {
                ulinCurState->callback(instance, LIN_READBACK_ERROR, ulinCurState);
            }

            /* Check if the transmission is non-blocking */
            if (ulinCurState->isTxBlocking == false) {
                /* Clear Tx busy flag */
                ulinCurState->isTxBusy = false;

                /* Change node's current state to IDLE */
                (void)UART_LIN_GotoIdleState(instance);
            }

            sendFlag = false;
        } else {
            ulinCurState->txBuff++;
            ulinCurState->cntByte++;
        }
    }

    if (sendFlag) {
        if (ulinCurState->cntByte < ulinCurState->txSize) {
            /* Send checksum byte */
            if ((ulinCurState->txSize - ulinCurState->cntByte) == 1U) {
                UART_REG_PutData(regBase, ulinCurState->checkSum);
            } else {
                /* Send data bytes */
                UART_REG_PutData(regBase, *ulinCurState->txBuff);
            }
        } else {
            ulinCurState->nodeState = LIN_NODE_STATE_SEND_DATA_COMPLETED;

            /* callback function to handle event TX COMPLETED */
            if (ulinCurState->callback != NULL) {
                ulinCurState->callback(instance, LIN_TX_COMPLETED, ulinCurState);
            }

            /* Check if the transmission is non-blocking */
            if (ulinCurState->isTxBlocking == false) {
                /* Clear Tx busy flag */
                ulinCurState->isTxBusy = false;

                /* In this case, node is in SLEEP state */
                if (ulinCurState->nodeState != LIN_NODE_STATE_SLEEP) {
                    /* Change node's current state to IDLE */
                    (void)UART_LIN_GotoIdleState(instance);
                }
            } else {
                /* Post Semaphore to signal Tx Completed*/
                (void)OS_Adapter_PostSemaphore(&ulinCurState->txCompleted);
            }
        }
    }
}

/*******************************************************************************
 *** Function Name : UART_LIN_MakeChecksumByte
 *** Description   : This function calculate checksum for a frame
 ******************************************************************************/
static uint8_t UART_LIN_MakeChecksumByte(uint32_t instance,
                                         const uint8_t* buffer,
                                         uint8_t sizeBuffer,
                                         uint8_t PID)
{
    uint8_t checkSum = PID;
    uint8_t retValue = 0U;
    const uint8_t* classicPID     = s_ulinConfigPtr[instance]->classicPID;
    const uint8_t numOfClassicPID = s_ulinConfigPtr[instance]->numOfClassicPID;

    /* Get list of PIDs use classic checksum. */
    if (numOfClassicPID == 0xFFU) {
        /*all frame use enhanced checksum */
        checkSum = 0U;
    } else {
        if (classicPID != NULL) {
            for (retValue = 0U; retValue < numOfClassicPID; retValue++) {
                if (checkSum == classicPID[retValue]) {
                    checkSum = 0U;
                    break;
                }
            }
        }
    }

    /* Calculate the checksum */
    retValue = LIN_MakeChecksumByte(buffer, sizeBuffer, checkSum);

    return retValue;
}

/*******************************************************************************
 *** Function Name : UART_LIN_GetBusPhyState
 *** Description   : Gets the bus external physical state
 ******************************************************************************/
lin_bus_state_t UART_LIN_GetBusPhyState(uint32_t instance)
{
    return (s_ulinBusState[instance]);
}

/*******EOF********************************************************************/
